<?php

use Itwmw\Attributes\Validate\DataValidatorInterface;
use Itwmw\Attributes\Validate\EventFunc;
use Itwmw\Attributes\Validate\PropertyValidator;
use Itwmw\Attributes\Validate\Provider\AttributesValidator;
use Itwmw\Validate\Exception\ValidateException;

/**
 * 验证属性参数
 *
 * @template T
 *
 * @param class-string<T>|object<T> $class
 * @param array                     $data
 * @param array<string>             $fields
 * @param array<EventFunc>          $before
 * @param array<EventFunc>          $after
 * @param string                    $scene
 *
 * @return object<T>
 *
 * @throws ReflectionException
 * @throws ValidateException
 */
function validate_attributes(string|object $class, array $data = [], array $fields = [], array $before = [], array $after = [], string $scene = ''): object
{
    $validator = new AttributesValidator();
    return $validator->validate($class, $data, $fields, $before, $after, $scene);
}

/**
 * 验证类方法
 *
 * @param class-string|object $class
 * @param string              $method
 * @param array               $data
 * @param string              $scene  验证场景，优先级没有注解高
 *
 * @return array<object>
 *
 * @throws ReflectionException
 * @throws ValidateException
 */
function validate_method(string|object $class, string $method, array $data = [], string $scene = ''): array
{
    if (!method_exists($class, $method)) {
        return [];
    }

    $methodRef  = new ReflectionMethod($class, $method);
    $attributes = $methodRef->getAttributes(PropertyValidator::class);
    $result     = [];
    if (!empty($attributes)) {
        foreach ($attributes as $attribute) {
            /** @var PropertyValidator $propertyValidator */
            $propertyValidator = $attribute->newInstance();
            $dataClass         = $propertyValidator->dataClass;
            $after             = $propertyValidator->getAfter();
            $before            = $propertyValidator->getBefore();
            $fields            = $propertyValidator->fields;
            $property_scene    = $propertyValidator->scene ?: $scene;
            $result[]          = validate_attributes($dataClass, $data, $fields, $before, $after, $property_scene);
        }
    }

    $params = $methodRef->getParameters();
    foreach ($params as $param) {
        $type = $param->getType();
        if ($type instanceof ReflectionNamedType && !$type->isBuiltin()) {
            $typeName = $type->getName();
            if (is_subclass_of($typeName, DataValidatorInterface::class)) {
                $result[] = validate_attributes($typeName, $data, scene: $scene);
            }
        }
    }

    return $result;
}
